/**
  * @(#)ProfileManager.java
  *
  * Provide a manager to consolidate the creation and retrieval of different
  * application profiles, such as university, retrieval, calculation,etc.
  *
  * @author Course Scheduler Team
  * 
  * @license GNU General Public License version 3 (GPLv3)
  *
  * This file is part of Course Scheduler, an open source, cross platform
  * course scheduling tool, configurable for most universities.
  *
  * Copyright (C) 2010-2012 Course Scheduler Team
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * 
  */
package com.pollicitus.scheduler.profiles;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.pollicitus.scheduler.core.Manager;
import com.pollicitus.scheduler.core.NamespaceSettings;
import com.pollicitus.scheduler.core.Settings;
import com.pollicitus.scheduler.profiles.Profile;
import com.pollicitus.scheduler.profiles.ProfileManager;

/**
 * Provide a manager to consolidate the creation and retrieval of different
 * application profiles, such as university, retrieval, calculation,etc.
 * 
 * @author Course Scheduler Team
 *
 */
public abstract class ProfileManager extends Manager {

	/**
	 * The most recent revision in which this file was changed in the source control system
	 */
	public static final String _revision = "$LastChangedRevision: 539 $";
	
	/**
	 * The date on which this file was last changed and submitted to source control
	 */
	public static final String _date = "$LastChangedDate: 2011-11-19 23:14:16 -0500 (Sat, 19 Nov 2011) $";
	
	/**
	 * The user which last changed the file and committed the change to source control
	 */
	public static final String _author = "$LastChangedBy: mike@pollicitus.com $";
	
	/**
	 * The URL which can be used to access this specific file within the source control system
	 */
	public static final String _headURL = "$HeadURL: https://courseschedulerx.googlecode.com/svn/trunk/course-scheduler/course-scheduler-core/src/main/java/com/pollicitus/scheduler/profiles/ProfileManager.java $";
	
	/**
	 * The source control file ID, comprised of the file name, latest revision, last changed date, and submitting user.
	 */
	public static final String _id = "$Id: ProfileManager.java 539 2011-11-20 04:14:16Z mike@pollicitus.com $";
	
	//protected class constants
	protected static final String NAMESPACE = "profiles";
	
	//protected property names
	protected static final String MANAGER_CLASS = "manager.implementation";
	protected static final String CONFIG_FILE = "manager.configuration";
	protected static final String MANAGER_NAMESPACE = "manager.namespace";
	
	//private class variables
	private static ProfileManager profManager;
	
	//protected instance variables for use by all implementations
	private Map<Class<? extends Profile>, Map<String, Profile>> profiles;
	
	/**
	 * Returns the Profile Manager as defined in the application properties files. 
	 * This requires retrieval of the implementation specified in the profile
	 * config and loading the class definition. The implementation settings are 
	 * loaded into the specified namespace, the instance is initialized and then 
	 * returned.
	 * 
	 * @param settings the application settings instance 
	 * @return the instantiated and initialized Profile Manager
	 * @throws ClassNotFoundException if the profile manager class is not found
	 * @throws IllegalAccessException  if the profile manager class cannot be accessed
	 * @throws InstantiationException if the profile manager class cannot be instantiated
	 * @throws IOException if there is an IO problem accessing the settings file
	 * @throws FileNotFoundException  if the settings file cannot be found
	 */
	public static ProfileManager getInstance(Settings settings) throws FileNotFoundException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException{
		//properly wrap the primary settings manager
		NamespaceSettings logProps = new NamespaceSettings(settings, NAMESPACE);
		
		//retrieve the logging settings
		String managerClass = logProps.getProperty(MANAGER_CLASS);
		String configFile = logProps.getProperty(CONFIG_FILE);
		String settingsNamespace = logProps.getProperty(MANAGER_NAMESPACE);
		
		//retrieve an initialized instance of the profile manager
		profManager = Manager.getInstance(settings, managerClass, configFile, settingsNamespace);
		
		return profManager;
	}
	
	/**
	 * Default constructor for the ProfileManager to properly instantiate the 
	 * instance variables which are owned by the abstract profile manager 
	 * implementation. This is the constructor which should be used by the
	 * Profile Manager implementations.
	 */
	protected ProfileManager(){
		profiles = new HashMap<Class<? extends Profile>, Map<String, Profile>>();
	}
	
	/**
	 * Retrieve the Profile corresponding to the specified profile name and type.
	 * The opening and closing of the profile is handled by the specified
	 * profile manager implementation. In order to optimize reuse of profiles, the 
	 * implementation can make use of the storage maps within this abstract profile
	 * manager
	 * 
	 * @param type the type of profile to retrieve
	 * @param name the profile name which should be retrieved.
	 * @return the specified profile
	 */
	public abstract <T extends Profile> T getProfile(Class<T> type, String name);
	
	/**
	 * Return a list of all the profiles of the specified type which the profile manager
	 * implementation is aware. This will be used during configuration and display
	 * for setting up different application components.
	 * 
	 * @param type the type of profiles to return
	 * @return a string [] representing the different profiles available
	*/
	public abstract <T extends Profile> T[] getProfileList(Class<T> type); 
	
	/**
	 * Create a new profile with the specified name (and type represented by T).
	 * This will be used during configuration when the user defines a new
	 * profile. The implementation defines the storage type and
	 * location.
	 * 
	 * @param type the type of profile to create
	 * @param name the profile to create
	 * @return the newly created profile
	 */
	public abstract <T extends Profile> T createProfile(Class<T> type, String name);
	
	/**
	 * Retrieve a memoized profile (if it exists) from the profile manager's storage
	 * 
	 * @param type the type of profile to return
	 * @param name the name of the profile to return
	 * @return the profile specified by type and name
	 */
	protected Profile getMemoizedProfile(Class<? extends Profile> type, String name){
		Map<String, Profile> typeProfiles = profiles.get(type);
		Profile profile = null;
		
		if(typeProfiles != null){
			profile = typeProfiles.get(name);
		}
		
		return profile;
	}
	
	/**
	 * Store a profile to the memoization structure in the profile manager for later use 
	 * 
	 * @param type the type of profile to store
	 * @param name the name of the profile
	 * @param profile the profile which should be stored
	 */
	protected void setMemoizedProfile(Class<? extends Profile> type, String name, Profile profile){
		Map<String, Profile> typeProfiles = profiles.get(type);
		
		if(typeProfiles == null){
			typeProfiles = new HashMap<String, Profile>();
			profiles.put(type, typeProfiles);
		}
		
		typeProfiles.put(name, profile);
	}
}
